//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ReShade 4.0 effect file
// visit facebook.com/MartyMcModding for news/updates
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Ambient Obscurance with Indirect Lighting "MXAO" 2.0 by Marty McFly
// CC BY-NC-ND 3.0 licensed.
// Modified by Marot for ReShade 4.0 compatibility and lightly optimized for the GShade project.
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Preprocessor Settings
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#ifndef MXAO_MIPLEVEL_AO
#define MXAO_MIPLEVEL_AO		0	//[0 to 2]      Miplevel of AO texture. 0 = fullscreen, 1 = 1/2 screen width/height, 2 = 1/4 screen width/height and so forth. Best results: IL MipLevel = AO MipLevel + 2
#endif

#ifndef MXAO_MIPLEVEL_IL
 #define MXAO_MIPLEVEL_IL		2	//[0 to 4]      Miplevel of IL texture. 0 = fullscreen, 1 = 1/2 screen width/height, 2 = 1/4 screen width/height and so forth.
#endif

#ifndef MXAO_ENABLE_IL
#define MXAO_ENABLE_IL			0	//[0 or 1]	Enables Indirect Lighting calculation. Will cause a major fps hit.
#endif

#ifndef MXAO_ENABLE_BACKFACE
#define MXAO_ENABLE_BACKFACE		1	//[0 or 1]	Enables back face check so surfaces facing away from the source position don't cast light. Will cause a major fps hit.
#endif

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// UI variables
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

uniform float fMXAOAmbientOcclusionAmount <
	ui_type = "slider";
	ui_min = 0.00; ui_max = 12.00;
        ui_label = "环境光遮蔽数量";
	ui_tooltip = "线性增加环境光遮蔽数量。如果设置得太高会导致最黑值丢失。";
> = 2.00;

uniform float fMXAOSampleRadius <
	ui_type = "slider";
	ui_min = 1.00; ui_max = 8.00;
        ui_label = "采样半径";
	ui_tooltip = "GI 的采样半径, 值更高的意味着更多的大尺度遮蔽，更少的小尺度细节。";
> = 2.50;

uniform float iMXAOSampleCount <
	ui_type = "slider";
	ui_min = 8.0; ui_max = 255.0;
	ui_step = 1.0;
        ui_label = "采样数";
	ui_tooltip = "MX环境光遮蔽采样数。值越高意味着更好的遮蔽效果与更少噪点，但会使性能下降。";
> = 24;

uniform float iMXAOBayerDitherLevel <
	ui_type = "slider";
	ui_min = 2; ui_max = 8;
        ui_label = "抖动大小";
	ui_tooltip = "\"随机\"旋转图案大小的因子。更高的系数意味着更少的明显的光晕，但环境光遮蔽更多噪点。将模糊步数设置为0，可以更好地看到效果。";
> = 3;

uniform float fMXAONormalBias <
	ui_type = "slider";
	ui_min = 0.0; ui_max = 0.8;
        ui_label = "法线偏移";
	ui_tooltip = "用于减少相互之间角度较小的表面的自身遮蔽。";
> = 0.2;

uniform bool bMXAOSmoothNormalsEnable <
        ui_label = "开启平滑法线";
	ui_tooltip = "开启平滑法线。 WIP.";
> = false;

uniform float fMXAOBlurSharpness <
	ui_type = "slider";
	ui_min = 0.00; ui_max = 5.00;
        ui_label = "模糊锐化";
	ui_tooltip = "环境光遮蔽锐化，越高意味着几何边缘越清晰，但环境光遮蔽越多噪点；越低意味着环境观遮蔽越平滑，但远处越模糊。";
> = 2.00;

uniform float fMXAOBlurSteps <
	ui_type = "slider";
	ui_min = 0; ui_max = 5;
        ui_label = "模糊步数";
	ui_tooltip = "环境光遮蔽双边模糊过滤器的偏移数。越高意味着环境光遮蔽越平滑，但也越模糊。";
> = 2;

uniform bool bMXAODebugViewEnable <
        ui_label = "开启Debug视角";
	ui_tooltip = "开启原始的环境光遮蔽/间接光输出来调试。";
> = false;

uniform float fMXAOFadeoutStart <
	ui_type = "slider";
        ui_label = "淡化开始";
	ui_min = 0.00; ui_max = 1.00;
	ui_tooltip = "淡化开始";
> = 0.2;

uniform float fMXAOFadeoutEnd <
	ui_type = "slider";
        ui_label = "淡化结束";
	ui_min = 0.00; ui_max = 1.00;
	ui_tooltip = "淡化结束";
> = 0.4;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Textures, Samplers
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#include "ReShade.fxh"
#define     AO_BLUR_GAMMA   2.0


texture2D texColorBypass 	{ Width = BUFFER_WIDTH; 			  Height = BUFFER_HEIGHT; 			    Format = RGBA8; MipLevels = 5+MXAO_MIPLEVEL_IL;};
texture2D texDistance 		{ Width = BUFFER_WIDTH; 			  Height = BUFFER_HEIGHT;  			    Format = R16F;  MipLevels = 5+MXAO_MIPLEVEL_AO;};
texture2D texSurfaceNormal	{ Width = BUFFER_WIDTH;                           Height = BUFFER_HEIGHT; 		            Format = RGBA8; MipLevels = 5+MXAO_MIPLEVEL_IL;};
sampler2D SamplerColorBypass	{	Texture = texColorBypass;	};
sampler2D SamplerDistance	{	Texture = texDistance;		};
sampler2D SamplerSurfaceNormal	{	Texture = texSurfaceNormal;	};

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Functions
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/* Fetches linearized depth value. depth data ~ distance from camera
   and 0 := camera, 1:= "infinite" distance, e.g. sky. */
float GetLinearDepth(float2 coords)
{
	return ReShade::GetLinearizedDepth(coords);
}

/* Fetches position relative to camera. This is somewhat inaccurate
   as it assumes FoV == 90 degrees but yields good enough results.
   Axes are multiplied with far plane to better scale the occlusion
   falloff and save instruction in AO main pass. Also using a bigger
   data range seems to reduce precision artifacts for logarithmic
   depth buffer option. */
float3 GetPosition(float2 coords)
{
	return float3(coords.xy*2.0-1.0,1.0)*GetLinearDepth(coords.xy)*RESHADE_DEPTH_LINEARIZATION_FAR_PLANE;
}

/* Same as above, except linearized and scaled data is already stored
   in dedicated texture and we're sampling mipmaps here. */
float3 GetPositionLOD(float2 coords, int mipLevel)
{
	return float3(coords.xy*2.0-1.0,1.0)*tex2Dlod(SamplerDistance, float4(coords.xy,0,mipLevel)).x;
}

/* Calculates normals based on partial depth buffer derivatives.
   Does a similar job to ddx/ddy but this is higher quality and
   it also takes care for object borders where usual ddx/ddy produce
   inaccurate normals.*/
float3 GetNormalFromDepth(float2 coords)
{
	const float3 offs = float3(BUFFER_PIXEL_SIZE.xy,0);

	const float3 f 	 =       GetPosition(coords.xy);
	float3 d_dx1 	 = - f + GetPosition(coords.xy + offs.xz);
	const float3 d_dx2 	 =   f - GetPosition(coords.xy - offs.xz);
	float3 d_dy1 	 = - f + GetPosition(coords.xy + offs.zy);
	const float3 d_dy2 	 =   f - GetPosition(coords.xy - offs.zy);

	d_dx1 = lerp(d_dx1, d_dx2, abs(d_dx1.z) > abs(d_dx2.z));
	d_dy1 = lerp(d_dy1, d_dy2, abs(d_dy1.z) > abs(d_dy2.z));

	return normalize(cross(d_dy1,d_dx1));
}

/* Box blur on normal map texture. Yes, it's as stupid as it sounds
   but helps nicely to get rid of too obvious geometry lines in
   landscape where a plain normal bias doesn't cut it. After all
   we're doing approximations over approximations. */
float3 GetSmoothedNormals(float2 texcoord, float3 ScreenSpaceNormals, float3 ScreenSpacePosition)
{
	float4 blurnormal = 0.0;
	[loop]
	for(float x = -3; x <= 3; x++)
	{
		[loop]
		for(float y = -3; y <= 3; y++)
		{
			const float2 offsetcoord 	= texcoord.xy + float2(x,y) * BUFFER_PIXEL_SIZE.xy * 3.5;
			const float3 samplenormal 	= normalize(tex2Dlod(SamplerSurfaceNormal,float4(offsetcoord,0,2)).xyz * 2.0 - 1.0);
			const float3 sampleposition	= GetPositionLOD(offsetcoord.xy,2);
			float weight 		= saturate(1.0 - distance(ScreenSpacePosition.xyz,sampleposition.xyz)*1.2);
			weight 		       *= smoothstep(0.5,1.0,dot(samplenormal,ScreenSpaceNormals));
			blurnormal.xyz += samplenormal * weight;
			blurnormal.w += weight;
		}
	}

	return normalize(blurnormal.xyz / (0.0001 + blurnormal.w) + ScreenSpaceNormals*0.05);
}

/* Fetches normal and depth data for bilateral AO blur weight
   calculation. As we already have linearized depth as own texture,
   we might as well use it and save some instructions, also R16F
   is lower than usual R32F original game depth data.*/
float4 GetBlurFactors(float2 coords)
{
	return float4(tex2Dlod(SamplerSurfaceNormal, float4(coords.xy,0,0)).xyz*2.0-1.0,tex2Dlod(SamplerDistance, float4(coords.xy,0,0)).x);
}

/* Calculates weights for bilateral AO blur. Using only
   depth is surely faster but it doesn't really cut it, also
   areas with a flat angle to the camera will have high depth
   differences, hence blur will cause stripes as seen in many
   AO implementations, even HBAO+. Taking view angle into
   account greatly helps to reduce these problems. */
float GetBlurWeight(float4 tempKey, float4 centerKey, float surfacealignment)
{
	const float depthdiff = abs(tempKey.w-centerKey.w);
	const float normaldiff = 1.0-saturate(dot(normalize(tempKey.xyz),normalize(centerKey.xyz)));

	const float depthweight = saturate(rcp(fMXAOBlurSharpness*depthdiff*5.0*surfacealignment));
	const float normalweight = saturate(rcp(fMXAOBlurSharpness*normaldiff*10.0));

	return min(normalweight,depthweight);
}

/* Bilateral blur, exploiting bilinear filter
   for additional blurring. Intel paper covered
   faster gaussian blur with similar offset and
   weight development of discrete gaussian, this
   here is basically the same, only applied on
   box blur. This function only blurs AO and reads
   the normals from RGB channel of backbuffer.*/
float4 GetBlurredAO( float2 texcoord, sampler inputsampler, float2 axis, int nSteps)
{
	float4 tempsample;
	float4 centerkey   , tempkey;
	float  centerweight, tempweight;
	float surfacealignment;
	float4 blurcoord = 0.0;
	float AO         = 0.0;

	tempsample 	 = tex2D(inputsampler,texcoord.xy);
	centerkey 	 = float4(tempsample.xyz*2-1,tex2Dlod(SamplerDistance,float4(texcoord.xy,0,0)).x);
	centerweight     = 0.5;
	AO               = tempsample.w * 0.5;
	surfacealignment = saturate(-dot(centerkey.xyz,normalize(float3(texcoord.xy*2.0-1.0,1.0)*centerkey.w)));

	[loop]
	for(int orientation=-1;orientation<=1; orientation+=2)
	{
		[loop]
		for(float iStep = 1.0; iStep <= nSteps; iStep++)
		{
			blurcoord.xy 	= (2.0 * iStep - 0.5) * orientation * axis * BUFFER_PIXEL_SIZE.xy + texcoord.xy;
			tempsample = tex2Dlod(inputsampler, blurcoord);
			tempkey    = float4(tempsample.xyz*2-1,tex2Dlod(SamplerDistance,blurcoord).x);
			tempweight = GetBlurWeight(tempkey, centerkey, surfacealignment);
			AO += tempsample.w * tempweight;
			centerweight   += tempweight;
		}
	}

	return float4(centerkey.xyz*0.5+0.5, AO / centerweight);
}

/* Same as above, except it blurs RGBA and hence
   needs to read normals separately. */
float4 GetBlurredAOIL( float2 texcoord, sampler inputsampler, float2 axis, int nSteps)
{
	float4 tempsample;
	float4 centerkey   , tempkey;
	float  centerweight, tempweight;
	float surfacealignment;
	float4 blurcoord = 0.0;
	float4 AO_IL         = 0.0;

	tempsample 	 = tex2D(inputsampler,texcoord.xy);
	centerkey 	 = float4(tex2Dlod(SamplerSurfaceNormal,float4(texcoord.xy,0,0)).xyz*2-1,tex2Dlod(SamplerDistance,float4(texcoord.xy,0,0)).x);
	centerweight     = 0.5;
	AO_IL            = tempsample * 0.5;
	surfacealignment = saturate(-dot(centerkey.xyz,normalize(float3(texcoord.xy*2.0-1.0,1.0)*centerkey.w)));

	[loop]
	for(int orientation=-1;orientation<=1; orientation+=2)
	{
		[loop]
		for(float iStep = 1.0; iStep <= nSteps; iStep++)
		{
			blurcoord.xy 	= (2.0 * iStep - 0.5) * orientation * axis * BUFFER_PIXEL_SIZE.xy + texcoord.xy;
			tempsample = tex2Dlod(inputsampler, blurcoord);
			tempkey    = float4(tex2Dlod(SamplerSurfaceNormal,blurcoord).xyz*2-1,tex2Dlod(SamplerDistance,blurcoord).x);
			tempweight = GetBlurWeight(tempkey, centerkey, surfacealignment);
			AO_IL += tempsample * tempweight;
			centerweight   += tempweight;
		}
	}

	return float4(AO_IL / centerweight);
}

/* Calculates the bayer dither pattern that's used to jitter
   the direction of the AO samples per pixel.
   Why this instead of precalculated texture? BECAUSE I CAN.
   Using this ordered jitter instead of a pseudorandom one
   has 3 advantages: it seems to be more cache-aware, the AO
   is (given a fitting AO sample distribution pattern) a lot less
   noisy (better variance, see Alchemy AO) and bilateral blur
   needs a much smaller kernel: from my tests a blur kernel
   of 5x5 is fine for most settings, but using a pseudorandom
   distribution still has noticeable grain with 12x12++.
   Smaller bayer matrix sizes have more obvious directional
   AO artifacts but are easier to blur. */
float GetBayerFromCoordLevel(float2 pixelpos, int maxLevel)
{
	float finalBayer = 0.0;

	for(float i = 1-maxLevel; i<= 0; i++)
	{
		const float bayerSize = exp2(i);
	        const float2 bayerCoord = floor(pixelpos * bayerSize) % 2.0;
		const float bayer = 2.0 * bayerCoord.x - 4.0 * bayerCoord.x * bayerCoord.y + 3.0 * bayerCoord.y;
		finalBayer += exp2(2.0*(i+maxLevel))* bayer;
	}

	const float finalDivisor = 4.0 * exp2(2.0 * maxLevel)- 4.0;
	//raising all values by increment is false but in AO pass it makes sense. Can you see it?
	return finalBayer/ finalDivisor + 1.0/exp2(2.0 * maxLevel);
}

/* Main AO pass. The samples are taken in an outward spiral,
   that way a simple rotation matrix is enough to provide
   the sample locations. The rotation angle is fine-tuned,
   it yields an optimal (optimal as in "I couldn't find a better one")
   sample distribution. Vogel algorithm uses the golden angle,
   and samples are more uniformly distributed over the disc but
   AO quality suffers a lot of samples are lining up (having the
   same sampling direction). Test it yourself: make angle depending
   on texcoord.x and you'll see that AO quality is highly depending
   on angle. Mara and McGuire solve this in their Alchemy AO approach
   by providing a hand-selected rotation for each sample count,
   however my angle seems to produce better results and doesn't require
   declaring a huge constant array or any CPU side code. */
float4 GetMXAO(float2 texcoord, float3 normal, float3 position, float nSamples, float2 currentVector, float mipFactor, float fNegInvR2, float radiusJitter, float sampleRadius)
{
	float4 AO_IL = 0.0;
	float2 currentOffset;

	[loop]
	for(int iSample=0; iSample < nSamples; iSample++)
	{
		currentVector = mul(currentVector.xy, float2x2(0.575,0.81815,-0.81815,0.575));
		currentOffset = texcoord.xy + currentVector.xy * float2(1.0,BUFFER_ASPECT_RATIO) * (iSample + radiusJitter);

		const float mipLevel = saturate(log2(mipFactor*iSample)*0.2 - 0.6) * 5.0;

		const float3 occlVec 		= -position + GetPositionLOD(currentOffset.xy, mipLevel);
		const float  occlDistanceRcp 	= rsqrt(dot(occlVec,occlVec));
		const float  occlAngle 	= dot(occlVec, normal)*occlDistanceRcp;

		const float fAO = saturate(1.0 + fNegInvR2/occlDistanceRcp)  * saturate(occlAngle - fMXAONormalBias);

		#if(MXAO_ENABLE_IL != 0)
			float3 fIL = tex2Dlod(SamplerColorBypass, float4(currentOffset,0,mipLevel + MXAO_MIPLEVEL_IL)).xyz;
			#if(MXAO_ENABLE_BACKFACE != 0)
				const float3 offsetNormals = normalize(tex2Dlod(SamplerSurfaceNormal, float4(currentOffset,0,mipLevel + MXAO_MIPLEVEL_IL)).xyz * 2.0 - 1.0);
				const float facingtoSource = dot(occlVec,offsetNormals)*occlDistanceRcp;
				fIL = fIL - fIL*saturate(facingtoSource*2.0);
			#endif
			AO_IL.w += fAO - fAO * saturate(dot(fIL,float3(0.299,0.587,0.114)));
			AO_IL.xyz += fIL*fAO;
		#else
			AO_IL.w += fAO;
		#endif
	}

	return saturate(AO_IL/(0.4*(1.0-fMXAONormalBias)*nSamples*sqrt(sampleRadius)));
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Pixel Shaders
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/* Setup color, depth and normal data. Alpha channel of normal
   texture provides the per pixel jitter for AO sampling. */
void PS_AO_Pre(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 color : SV_Target0, out float4 depth : SV_Target1, out float4 normal : SV_Target2)
{
	color 		= tex2D(ReShade::BackBuffer, texcoord.xy);
	depth 		= GetLinearDepth(texcoord.xy)*RESHADE_DEPTH_LINEARIZATION_FAR_PLANE;
	normal.xyz 	= GetNormalFromDepth(texcoord.xy).xyz * 0.5 + 0.5;
	normal.w	= GetBayerFromCoordLevel(vpos.xy,iMXAOBayerDitherLevel);
}

void PS_AO_Gen(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 res : SV_Target0)
{
	const float4 normalSample = tex2D(SamplerSurfaceNormal, texcoord.xy);

	float3 ScreenSpaceNormals = normalSample.xyz * 2.0 - 1.0;
	float3 ScreenSpacePosition = GetPositionLOD(texcoord.xy, 0);

	if(bMXAOSmoothNormalsEnable)
	{
		ScreenSpaceNormals = GetSmoothedNormals(texcoord, ScreenSpaceNormals, ScreenSpacePosition);
	}

	const float scenedepth = ScreenSpacePosition.z / RESHADE_DEPTH_LINEARIZATION_FAR_PLANE;
	ScreenSpacePosition += ScreenSpaceNormals * scenedepth;

	const float SampleRadiusScaled  = 0.2*fMXAOSampleRadius*fMXAOSampleRadius / (iMXAOSampleCount * ScreenSpacePosition.z);
	const float mipFactor = SampleRadiusScaled * 3200.0;

	float2 currentVector;
	sincos(2.0*3.14159274*normalSample.w, currentVector.y, currentVector.x);
	const float fNegInvR2 = -1.0/(fMXAOSampleRadius*fMXAOSampleRadius);
	currentVector *= SampleRadiusScaled;

	res = GetMXAO(texcoord,
		      ScreenSpaceNormals,
		      ScreenSpacePosition,
		      iMXAOSampleCount,
		      currentVector,
		      mipFactor,
		      fNegInvR2,
		      normalSample.w,
		      fMXAOSampleRadius);

	res = sqrt(abs(res));

	#if(MXAO_ENABLE_IL == 0)
		res.xyz = normalSample.xyz;
	#endif
}

/* Box blur instead of gaussian seems to produce better
   results for low kernel sizes. The offsets and weights
   here make use of bilinear sampling, hence sampling
   in 1.5 .. 3.5 ... 5.5 pixel offsets.*/
void PS_AO_Blur1(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 res : SV_Target0)
{
	#if(MXAO_ENABLE_IL != 0)
		res = GetBlurredAOIL(texcoord.xy, ReShade::BackBuffer, float2(1.0,0.0), fMXAOBlurSteps);
	#else
		res = GetBlurredAO(texcoord.xy, ReShade::BackBuffer, float2(1.0,0.0), fMXAOBlurSteps);
	#endif
}

/* Second box blur pass and AO/IL combine. The given formula
   yields to actual physical background or anything, it's just
   a lot more visually pleasing than most formulas of similar
   implementations.*/
void PS_AO_Blur2(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 res : SV_Target0)
{
	#if(MXAO_ENABLE_IL != 0)
		float4 MXAOFFXIV = GetBlurredAOIL(texcoord.xy, ReShade::BackBuffer, float2(0.0,1.0), fMXAOBlurSteps);
		MXAOFFXIV = pow(saturate(MXAOFFXIV),AO_BLUR_GAMMA);
	#else
		float4 MXAOFFXIV = GetBlurredAO(texcoord.xy, ReShade::BackBuffer, float2(0.0,1.0), fMXAOBlurSteps);
		MXAOFFXIV.xyz = 0;
		MXAOFFXIV.w = pow(saturate(MXAOFFXIV.w),AO_BLUR_GAMMA);
	#endif

	const float scenedepth = GetLinearDepth(texcoord.xy);
	float4 color = saturate(tex2D(SamplerColorBypass, texcoord.xy));
	const float colorgray = dot(color.xyz,float3(0.299,0.587,0.114));

	
	MXAOFFXIV.w    = 1.0-pow(1.0-MXAOFFXIV.w, fMXAOAmbientOcclusionAmount*4.0);

	if (!bMXAODebugViewEnable)
		MXAOFFXIV = lerp(MXAOFFXIV, 0.0, pow(colorgray,2.0));

	MXAOFFXIV.w    = lerp(MXAOFFXIV.w, 0.0,smoothstep(max(fMXAOFadeoutStart, 0.0001), max(fMXAOFadeoutEnd, 0.0001), max(scenedepth, 0.0001)));
	MXAOFFXIV.xyz  = lerp(MXAOFFXIV.xyz,0.0,smoothstep(max(fMXAOFadeoutStart, 0.0001)*0.5, max(fMXAOFadeoutEnd, 0.0001)*0.5, max(scenedepth, 0.0001)));

	float3 GI = MXAOFFXIV.w - MXAOFFXIV.xyz;
	GI = saturate(1-GI);
	color.xyz *= GI;

	if(bMXAODebugViewEnable) //can't move this into ternary as one is preprocessor def and the other is a uniform
	{
		if (MXAO_ENABLE_IL != 0)
			color.xyz = GI*0.5;
		else
			color.xyz = GI;
	}

	res = color;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Technique
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

technique MXAOFFXIV <ui_label="MX环境光遮蔽FFXIV(MXAO)";ui_tooltip="环境光遮蔽(AO)与间接光的结合";>
{
	pass P0
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_AO_Pre;
		RenderTarget0 = texColorBypass;
		RenderTarget1 = texDistance;
		RenderTarget2 = texSurfaceNormal;
	}
	pass P1
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_AO_Gen;
		/*Render Target is Backbuffer*/
	}
	pass P2_0
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_AO_Blur1;
		/*Render Target is Backbuffer*/
	}
	pass P2_1
	{
		VertexShader = PostProcessVS;
		PixelShader  = PS_AO_Blur2;
		/*Render Target is Backbuffer*/
	}
}
